import requests
import json
import base64
import hmac
import hashlib
import time
import decimal
import urllib
import os
from datetime import datetime
from ..exceptions import PlatformAPIException, PlatformRequestException, PlatformNotSupportSymbolException, PlatformClientException
from Platforms.key import ACCOUNT_ID, COMMODITY, BASE
from .symbols import symbols

class HuobiRESTAPI(object):
    """docstring for BitfinexRESTAPI"""

    def __init__(self, api_key, secret_key, isdebug=False):
        self.api_key = api_key
        self.api_secret = secret_key
        self.isdebug = isdebug
        self.url = 'https://api.huobi.pro'

    def _debug(self, result):
        if self.isdebug:
            print(result)

    def _get_auth(self, method, endpoint, params={}):
        host = 'api.huobi.pro'

        params['SignatureMethod'] = 'HmacSHA256'
        params['SignatureVersion'] = '2'
        params['AccessKeyId'] = self.api_key
        params['Timestamp'] = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
        # sort by key:
        keys = sorted(params.keys())
        # build query string like: a=1&b=%20&c=:
        qs = '&'.join(['%s=%s' % (key, self._encode(params[key]))
                       for key in keys])
        # build payload:
        payload = '%s\n%s\n%s\n%s' % (method, host, endpoint, qs)
        # self._debug('payload:\n%s' % payload)

        dig = hmac.new(self.api_secret.encode(
            'utf-8'), msg=payload.encode('utf-8'), digestmod=hashlib.sha256).digest()
        signature = self._encode(base64.b64encode(dig).decode())
        # self._debug('Signature: ' + signature)

        signeddata = qs + '&Signature=' + signature
        return signeddata

    def _get(self, endpoint, signed=False, params={}):
        url = self.url + endpoint
        self._debug(url)

        response = None
        if signed:
            urlparams = self._get_auth('GET', endpoint, params)
            response = requests.get(url + '?' + urlparams)
        else:
            response = requests.get(url, params)

        rescode = response.status_code
        restext = response.text
        self._debug(str(rescode) + '|' + restext)

        errmsg = 'StatusCode: GET %s \nURL:%s \nParams: %s \nResponse: %s' % (
            response.status_code, url, str(params), response.text)
        response = response.json()

        if response:
            if response['status'] == 'ok':
                return response
            elif response['status'] == 'error':
                raise PlatformAPIException(errmsg)
        else:
            raise PlatformRequestException(errmsg)

    def _post(self, endpoint, data={}):
        url = self.url + endpoint
        self._debug(url)
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        }
        urlparams = self._get_auth('POST', endpoint)
        data = json.dumps(data).encode('utf-8')
        response = requests.post(
            url + '?' + urlparams, headers=headers, data=data)

        rescode = response.status_code
        restext = response.text
        self._debug(str(rescode) + '|' + restext)

        errmsg = 'StatusCode: POST %s \nURL:%s \nParams: %s \nResponse: %s' % (
            response.status_code, url, str(data), response.text)
        response = response.json()

        if response:
            if response['status'] == 'ok':
                return response
            elif response['status'] == 'error':
                raise PlatformAPIException(errmsg)
        else:
            raise PlatformRequestException(errmsg)

    # def _auth_data(self):
    #     md5 = hashlib.md5()
    #     md5.update(self._assetPassword.encode('utf-8'))
    #     md5.update('hello, moto'.encode('utf-8'))
    #     s = json.dumps({"assetPwd": md5.hexdigest()})
    #     return self._encode(s)

    def _encode(self, s):
        return urllib.parse.quote(s, safe='')

    # EndPoints
    def _symbols(self):
        url = '/v1/common/symbols'

        resjson = self._get(url, True)
        result = []
        for res in resjson["data"]:
            result.append(res["base-currency"] + res["quote-currency"])

        sw = open(os.path.dirname(
            os.path.realpath(__file__)) + '/symbols.py', 'w')
        sw.write('symbols = ' + str(result))
        sw.close()

        self._debug('currency_pair: \n' + json.dumps(result, indent=2))
        return result

    def _order(self, symbol, side, ordertype, amount, price):
        url = '/v1/order/orders/place'

        data = {
            'account-id': ACCOUNT_ID["huobi"],
            'amount': amount,
            'source': 'api',
            'symbol': symbol,
            'type': side + '-' + ordertype
        }

        if ordertype.lower() == 'limit':
            data['price'] = price

        result = self._post(url, data)
        # self._debug('new_order: \n' + json.dumps(result, indent=2))
        return result

    def _batch_cancel(self, order_ids):
        url = '/v1/order/orders/batchcancel'

        data = {
            'order-ids': order_ids,
        }

        result = self._post(url, data)
        # self._debug('batch_cancel: \n' + json.dumps(result, indent=2))
        return result

    # Public
    def get_currency(self, currency):
        return currency.lower()

    def get_symbol(self, commodity, base):
        result = commodity.lower() + base.lower()
        if not (result in symbols):
            errmsg = 'Not supported: %s' % result
            raise PlatformNotSupportSymbolException(errmsg)
        return result

    def get_ticker(self, symbol):
        url = '/market/detail/merged'

        params = {
            'symbol': symbol
        }

        result = self._get(url, False, params)
        self._debug('ticker: \n' + json.dumps(result, indent=2))
        return result

    def get_depth(self, symbol):
        url = '/market/depth'

        params = {
            'symbol': symbol,
            'type': 'step0'
        }

        resjson = self._get(url, False, params)

        bids = []
        asks = []
        result = {}

        for depth in resjson['tick']['bids']:
            bids.append({'price': depth[0], 'amount': depth[1]})

        for depth in resjson['tick']['asks']:
            asks.append({'price': depth[0], 'amount': depth[1]})

        result = {'bids': bids, 'asks': asks}

        self._debug('order_book: \n' + json.dumps(result, indent=2))
        return result

    def get_account(self):
        url = '/v1/account/accounts'

        result = self._get(url, True)
        self._debug('account: \n' + json.dumps(result, indent=2))
        return result

    def get_balances(self, type='trade'):
        url = '/v1/account/accounts'

        accounts = self._get(url, True)

        result = {}

        if type == 'trade':
            for account in accounts['data']:
                if account['type'] == 'spot':
                    url = '/v1/account/accounts/' + str(account['id']) + '/balance'
                    resjson = self._get(url, True)

                    for balance in resjson['data']['list']:
                        if balance['type'] == 'trade':
                            result[balance['currency']] = float(balance['balance'])

                    break
        elif type == 'margin':
            for account in accounts['data']:
                if account['type'] == 'margin':
                    url = '/v1/account/accounts/' + str(account['id']) + '/balance'
                    resjson = self._get(url, True)

                    for balance in resjson['data']['list']:
                        if balance['currency'] in result:
                            result[balance['currency']] += float(balance['balance'])
                        else:
                            result[balance['currency']] = float(balance['balance'])

        self._debug('balance: \n' + json.dumps(result, indent=2))
        return result

    def limit_buy(self, symbol, amount, price):
        resjson = self._order(symbol, 'buy', 'limit', str(amount), float(price))
        result = {"id": resjson['data']}
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def limit_sell(self, symbol, amount, price):
        resjson = self._order(symbol, 'sell', 'limit', str(amount), float(price))
        result = {"id": resjson['data']}
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def market_buy(self, symbol, amount, price):
        # market buy order is on base currency.
        resjson = self._order(symbol, 'buy', 'market', "%.5f" % (float(amount) * float(price)), str(price))
        result = {"id": resjson['data']}
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def market_sell(self, symbol, amount, price):
        # market sell order is on commodity currency.
        resjson = self._order(symbol, 'sell', 'market', str(amount), str(price))
        result = {"id": resjson['data']}
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def get_order(self, symbol, order_id):
        url = '/v1/order/orders/' + order_id

        result = self._get(url, True)
        self._debug('[GET] get_order: \n' + json.dumps(result, indent=2))
        return result

    def get_all_orders(self, symbol):
        url = '/v1/order/orders'

        legal_symbols = []
        if symbol == '':
            for ba in BASE:
                for com in COMMODITY:
                    if ba != com:
                        try:
                            legal_symbols.append(self.get_symbol(com, ba))
                        except PlatformNotSupportSymbolException:
                            pass
        else:
            legal_symbols.append(symbol)

        result = []

        for sym in legal_symbols:
            params = {
                'symbol': sym,
                'states': 'pre-submitted,submitted,partial-filled,partial-canceled,filled,canceled'
            }
            resjson = self._get(url, True, params)['data']
            for res in resjson:
                result.append(res)
            self._debug('[GET] get_open_orders: ' + sym + '\n' +
                        json.dumps(resjson, indent=2))
            time.sleep(1)

        self._debug('[GET] get_open_orders:\n' + json.dumps(result, indent=2))
        return result

    def get_open_orders(self, symbol=''):
        """[summary]

        [get open orders, include pre-submitted,submitted,partial-filled]

        Arguments:
            symbol {[string]} -- [optional, when '' return all]

        Returns:
            [dict] -- [raw response]
        """

        url = '/v1/order/orders'

        legal_symbols = []
        if symbol == '':
            for ba in BASE:
                for com in COMMODITY:
                    if ba != com:
                        try:
                            legal_symbols.append(self.get_symbol(com, ba))
                        except PlatformNotSupportSymbolException:
                            pass
        else:
            legal_symbols.append(symbol)

        result = []

        for sym in legal_symbols:
            params = {
                'symbol': sym,
                'states': 'pre-submitted,submitted,partial-filled'
            }
            resjson = self._get(url, True, params)['data']
            for res in resjson:
                result.append(res)
            self._debug('[GET] get_open_orders: ' + sym + '\n' +
                        json.dumps(resjson, indent=2))
            time.sleep(1)

        self._debug('[GET] get_open_orders:\n' + json.dumps(result, indent=2))
        return result

    def cancel_order(self, symbol, order_id):
        url = '/v1/order/orders/' + order_id + '/submitcancel'

        result = self._post(url)
        self._debug('cancel_order: \n' + json.dumps(result, indent=2))
        return result

    def cancel_all_orders(self, symbol=''):
        order_ids = []
        orders = self.get_open_orders(symbol)

        if len(orders) == 0:
            return "order_list_is_none"

        for order in orders:
            order_ids.append(order['id'])

        totaltimes = 50

        result = True
        if len(order_ids) < totaltimes:
            resjson = self._batch_cancel(order_ids)
            if "failed" in resjson and len(resjson["failed"]) > 0:
                result = resjson
            self._debug('batch_cancel: \n' + json.dumps(resjson, indent=2))
        else:
            turns = int(len(order_ids) / totaltimes)
            for index in range(0, turns + 1):
                order_ids_slice = order_ids[index *
                                            totaltimes:(index + 1) * totaltimes]
                resjson = self._batch_cancel(order_ids_slice)
                if "failed" in resjson and len(resjson["failed"]) > 0:
                    result = resjson
                self._debug('batch_cancel: \n' + json.dumps(resjson, indent=2))

        return result

    def withdraw(self, currency, amount, address):
        url = '/v1/dw/withdraw/api/create'

        data = {
            'currency': currency,
            'amount': amount,
            'address': address,
        }

        result = self._post(url, data)
        self._debug('transfer: \n' + json.dumps(result, indent=2))
        return result
